# ommbot/plugins.py
#
#

""" provide plugin infrastructure """

__copyright__ = 'this file is in the public domain'

from ommbot.myimport import my_import
from ommbot.world import rlog
import ommbot.generic
import ommbot.redispatcher
import ommbot.examples
import ommbot.callbacks
import ommbot.commands
import ommbot.aliases
import ommbot.persist
import ommbot.users
import ommbot.thr
import os
import glob

class Plugins(object):

    """ hold all the plugins """

    def __init__(self, datadir):
        self.datadir = str(datadir)
        self.cmnds = ommbot.commands.Botcommands()
        self.rebefore = ommbot.redispatcher.Botredispatcher()
        self.reafter = ommbot.redispatcher.Botredispatcher()
        self.callbacks = ommbot.callbacks.Botcallbacks()
        self.aliases = ommbot.aliases.Aliases(datadir)
        self.examples = ommbot.examples.Examples()
        self.users = ommbot.users.Users(datadir)
        self.plugs = {}
        self.paths = {}
        self.plugpaths = []
        self.plugdeny = ommbot.persist.Persist(datadir + '/plugdeny')
        if not self.plugdeny.data:
            self.plugdeny.data = []

    def __getitem__(self, item):
        """ return plugin """
        if self.plugs.has_key(item):
            return self.plugs[item]

    def restart(self):
        rlog(10, 'plugins', 'restarting')
        mods = self.plugs
        paths = self.paths
        plugpaths = self.plugpaths
        self.__init__(self.datadir)
        self.paths = paths
        self.plugs = mods
        self.plugpaths = plugpaths
        self.reloadall()

    def exist(self, name):
        """ see if plugin <name> exists """
        if self.plugs.has_key(name):
            return 1 

    def disable(self, name):
        """ prevent plugins <name> to be loaded """
        if name not in self.plugdeny.data:
            self.plugdeny.data.append(name)
            self.plugdeny.save()

    def enable(self, name):
        """ enable plugin <name> """
        if name in self.plugdeny.data:
            self.plugdeny.data.remove(name)
            self.plugdeny.save()

    def list(self):
        """ list of registered plugins """
        return self.plugs.keys()

    def regplugin(self, path):
        """ register plugin """
        name = path.split(os.sep)[-1][:-3]
        # see if plugin is in deny
        if name in self.plugdeny.data:
            return 0
        self.paths[name] = path
        # if plugin is already registered unload it
        if self.plugs.has_key(name):
            self.unloadnosave(name)
        rlog(0, 'plugins', 'register %s' % name)        
        # import the plugin
        try:
            plug = my_import(path[:-3].replace(os.sep,'.'))
            self.plugs[name] = plug
        except Exception, ex:
            self.unload(name)
            raise
        # call init function in plugin
        try:
            result = plug.init()
            if not result:
                return 
        except AttributeError:
            pass
        except:
            self.unload(name)
            raise
        return 1

    def regplugins(self, path):
        """ register plugins """
        # first do plugins in the "plugs" directory
        # if database is enabled check "dbplugs" directory
        if not os.path.isdir(path):
            return
        if path not in self.plugpaths:
            self.plugpaths.append(path)
        for i in glob.glob(path + os.sep + '*.py'):
            if '__' in i:
                continue
            try:
                self.regplugin(i)
            except Exception, ex:
                ommbot.generic.handle_exception()
        # log loaded plugs
        pluglist = self.list()
        pluglist.sort()
        # see if there is a permoverload file and if so use it to overload
        # permissions based on function name
        try:
            overloadfile = open('permoverload','r')
        except IOError:
            return
        try:
            for i in overloadfile:
                (funcname, perm) = i.split(',', 1)
                perm = perm.strip()
                self.permoverload(funcname, perm)
        except:
            handle_exception()

    def saveplug(self, plugname):
        """ call save on items in plugins savelist """
        try:
            savelist = self.plugs[plugname].savelist
            for saveitem in savelist:
                try:
                    saveitem.save()
                except:
                    handle_exception()
        except AttributeError:
            pass
        except KeyError:
            pass

    def save(self):
        """ call registered plugins save """
        for plug in self.plugs.values():
            try:
                savelist = getattr(plug, 'savelist')
            except AttributeError:
                continue
            for item in savelist:
                try:
                    item.save()
                except:
                    handle_exception()
    def exit(self):
        """ call registered plugins save """
        self.save()
        threadlist = []
        for name, plug in self.plugs.iteritems():
            try:
                shutdown = getattr(plug, 'shutdown')
                thread = ommbot.thr.start_new_thread(shutdown,())
                threadlist.append((name, thread))
            except AttributeError:
                continue
	for name, thread in threadlist:
	    thread.join()

    def reloadall(self):
        reloaded = []
        for i,j in self.plugs.iteritems():
            try:
                j.shutdown()
            except AttributeError:
                pass
            except Exception, ex:
                handle_exception()
            mod = reload(j)
            try:
                mod.init()
            except AttributeError:
                pass
            except Exception, ex:
                handle_exception()
            reloaded.append(i)
        rlog(0, 'plugins', 'reloaded %s' % reloaded)

    def reload(self, path, name):
        """ reload plugin """
        ok = 0
        # save data in plugin's savelist before we try to reload
        modname = path + '.' + name
        # unload plugin
        self.unload(name)
        # see if module is already loaded if not load it
        if not self.plugs.has_key(name):
            self.plugs[name] = my_import(modname)
        # reload the thing
        self.plugs[name] = reload(self.plugs[name])
        # call init now the module has been reloaded
        try:
            ok = self.plugs[name].init()
        except (AttributeError, KeyError):
            ok = 1
            pass
        # enable plugin
        self.enable(name)
        if not ok:
            return 0
        return 1

    def unload(self, plugname):
        """ unload plugin """
        # call plugins shutdown function if available
        try:
            self.plugs[plugname].shutdown()
        except (AttributeError, KeyError):
            pass
        self.saveplug(plugname)
        return self.unloadnosave(plugname)

    def unloadnosave(self, plugname):
        """ unload plugin without saving """
        try:
            self.cmnds.unload(plugname)
            self.callbacks.unload(plugname)
            self.rebefore.unload(plugname)
            self.reafter.unload(plugname)
            if self.plugs.has_key(plugname):
                del self.plugs[plugname]
        except Exception, ex:
            ommbot.generic.handle_exception()
            return 0
        return 1

    def permoverload(self, funcname, perm):
        """ overload permission of a function """
        if not rebefore.permoverload(funcname, perm):
            if not cmnds.permoverload(funcname, perm):
                if not reafter.permoverload(funcname, perm):
                    return 0
        return 1

    def woulddispatch(self, bot, ievent):
        """ see if ievent would dispatch """
        result = []
        com = self.rebefore.getcallback(ievent.txt)
        if com:
            result = (self.rebefore, com)
        else:
            com = self.cmnds.getcommand(ievent.txt)
            if com:
                result = (self.cmnds, com)
            else:
                com = self.reafter.getcallback(ievent.txt)
                if com:
                    result = (self.reafter, com)
        if result:
            if bot.users.allowed(ievent.userhost, result[1].perms):
                return result
        return (None, None)

    def trydispatch(self, bot, ievent):
        """ try to dispatch ievent """
        self.aliases.check(ievent)
        (what, com) = self.woulddispatch(bot, ievent)
        if what:
            return self.dispatch(what, com, bot, ievent)
        else:
            ievent.reply("can't dispatch %s" % ievent.txt) 

    def dispatch(self, what, com, bot, ievent):
        """ dispatch ievent """
        if bot.stop:
            return 0
        # check for user provided channel
        ievent.bot = bot
        if not ievent.sock:
            ievent.sock = bot.sock
        rlog(10, 'plugins', '%s (%s) dispatching %s' % (ievent.nick, \
                 ievent.userhost, ievent.txt))
        bot.commandhandler.put(ievent.speed, what, com, bot, ievent)
        return 1
